﻿using System.Collections.Generic;
using System.Xml;

using pt = System.UInt32;

namespace RDLC
{
   public enum RDLDataElementStyle
   {
      Attribute,
      Element
   };  

   /// <summary>
   /// Definition taken from SQL Server Reporting Services 2005 Specification
   /// http://msdn.microsoft.com/en-us/library/ms153590(SQL.90).aspx
   /// 
   /// Constructor parameters are required elements.  All other elements are optional.
   /// </summary>
   public class RDLReport
   {
      const uint PT_PER_IN = 72;

      public RDLReport(RDLBody body, pt width)
      {
         // Set non-optional properties
         Body = body;
         Width = width;
      }
      
      /// <summary>
      /// Specifies the name of the person who created the report.
      /// </summary>
      public string Author; // Optional
      /// <summary>
      /// The rate at which the report refreshes
      /// </summary>
      public uint? AutoRefresh
      {
         get { return _AutoRefresh; }
         set
         {
            if (!_AutoRefresh.HasValue)
               _AutoRefresh = new uint();
            _AutoRefresh = value;
         }
      }
      private uint? _AutoRefresh;
      /// <summary>
      /// Contains the visual elements of the report.
      /// </summary>
      public RDLBody Body;
      public pt? BottomMargin
      {
         get { return _BottomMargin; }
         set
         {
            if (!_BottomMargin.HasValue)
               _BottomMargin = new pt();
            _BottomMargin = value;
         }
      }
      private pt? _BottomMargin;
      //note: Classes element not implemented
      public string Code; // Optional
      //note: CodeModules element not implemented
      //note: Custom element not implemented
      public string DataElementName; // Optional
      public RDLDataElementStyle? DataElementStyle
      {
         get { return _DataElementStyle; }
         set
         {
            if (!_DataElementStyle.HasValue)
               _DataElementStyle = new RDLDataElementStyle();
            _DataElementStyle = value;
         }
      }
      public RDLDataElementStyle? _DataElementStyle;
      public string DataSchema; // Optional
      public List<DataSet> DataSets = new List<DataSet>(); // Optional
      public List<DataSource> DataSources = new List<DataSource>(); // Optional
      public string DataTransform; // Optional
      public bool? DeferVariableEvaluation
      {
         get { return _DeferVariableEvaluation; }
         set
         {
            if (!_DeferVariableEvaluation.HasValue)
               _DeferVariableEvaluation = new bool();
            _DeferVariableEvaluation = value;
         }
      }
      private bool? _DeferVariableEvaluation;
      public string Description; // Optional
      // Only required if any report items are using embedded images
      public List<EmbeddedImage> EmbeddedImages = new List<EmbeddedImage>(); 
      //note: Language element not implemented
      public pt? LeftMargin
      {
         get { return _LeftMargin; }
         set
         {
            if (!_LeftMargin.HasValue)
               _LeftMargin = new pt();
            _LeftMargin = value;
         }
      }
      private pt? _LeftMargin;
      public PageHeaderFooter Footer; // Optional
      public PageHeaderFooter Header; // Optional
      public pt? PageHeight
      {
         get { return _PageHeight; }
         set
         {
            if (!_PageHeight.HasValue)
               _PageHeight = new pt();
            _PageHeight = value;
         }
      }
      private pt? _PageHeight;
      public pt? PageWidth
      {
         get { return _PageWidth; }
         set
         {
            if (!_PageWidth.HasValue)
               _PageWidth = new pt();
            _PageWidth = value;
         }
      }

      private pt? _PageWidth;
      //note: ReportParameters element not implemented
      public pt? RightMargin
      {
         get { return _RightMargin; }
         set
         {
            if (!_RightMargin.HasValue)
               _RightMargin = new pt();
            _RightMargin = value;
         }
      }

      private pt? _RightMargin;
      public pt? TopMargin
      {
         get { return _TopMargin; }
         set
         {
            if (!_TopMargin.HasValue)
               _TopMargin = new pt();
            _TopMargin = value;
         }
      }

      private pt? _TopMargin;
      public pt Width;

      public void Write(XmlTextWriter writer)
      {        
         writer.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");
         writer.WriteStartElement("Report");
         writer.WriteAttributeString("xmlns", null,
             "http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition");

         writer.WriteElementString("Language", "en-US");

         if (DataSources.Count != 0)
         {
            writer.WriteStartElement("DataSources");

            foreach (DataSource dataSource in DataSources)
            {
               dataSource.Write(writer);
            }

            writer.WriteEndElement(); // DataSources
         }

         if (DataSets.Count != 0)
         {
            writer.WriteStartElement("DataSets");

            foreach (DataSet dataSet in DataSets)
            {
               dataSet.Write(writer);
            }

            writer.WriteEndElement(); // DataSets
         }

         if (Author != null)
            writer.WriteElementString("Author", Author);

         if (AutoRefresh.HasValue)
            writer.WriteElementString("AutoRefresh", AutoRefresh.Value.ToString());

         if (BottomMargin.HasValue)
            writer.WriteElementString("BottomMargin", BottomMargin.Value + "pt");

         if (Code != null)
            writer.WriteElementString("Code", Code);

         if (DataElementName != null)
            writer.WriteElementString("DataElementName", DataElementName);

         if (DataElementStyle.HasValue)
            writer.WriteElementString("DataElementStyle", DataElementStyle.Value.ToString());

         if (DataSchema != null)
            writer.WriteElementString("DataSchema", DataSchema);

         if (DataTransform != null)
            writer.WriteElementString("DataTransform", DataTransform);

         if (DeferVariableEvaluation.HasValue)
            writer.WriteElementString("DeferVariableEvaluation", DeferVariableEvaluation.Value.ToString());

         if (Description != null)
            writer.WriteElementString("Description", Description);

         if (LeftMargin.HasValue)
            writer.WriteElementString("LeftMargin", LeftMargin.Value + "pt");

         if (PageHeight.HasValue)
         {
            writer.WriteElementString("PageHeight", PageHeight.Value + "pt");
            writer.WriteElementString("InteractiveHeight", PageHeight.Value + "pt");
         }

         if (PageWidth.HasValue)
         {
            writer.WriteElementString("PageWidth", PageWidth.Value + "pt");
            writer.WriteElementString("InteractiveWidth", PageWidth.Value + "pt");
         }

         if (RightMargin.HasValue)
            writer.WriteElementString("RightMargin", RightMargin.Value + "pt");

         if (TopMargin.HasValue)
            writer.WriteElementString("TopMargin", TopMargin.Value + "pt");

         writer.WriteElementString("Width", Width + "pt");

         if(EmbeddedImages.Count > 0)
         {
            writer.WriteStartElement("EmbeddedImages");
            foreach (var embeddedImage in EmbeddedImages)
            {
               embeddedImage.Write(writer);
            }
            writer.WriteEndElement(); // EmbeddedImages
         }

         if(Header != null)
            Header.Write(writer);
         
         Body.Write(writer);

         if(Footer != null)
            Footer.Write(writer);

         writer.WriteEndElement(); // Report

         // Flush the writer and close the stream
         writer.Flush();
         writer.Close();
      }
   }

   public class EmbeddedImage
   {
      private readonly string _Name;
      private readonly string _ImageData;
      private readonly RDLStyle.ImageMimeType _ImageMimeType;

      public EmbeddedImage(string name, RDLStyle.ImageMimeType imageType, string imageData)
      {
         _Name = name;
         _ImageMimeType = imageType;
         _ImageData = imageData;
      }

      public void Write(XmlTextWriter writer)
      {
         writer.WriteStartElement("EmbeddedImage");
         writer.WriteAttributeString("Name", null, _Name);

         writer.WriteElementString("MIMEType", null, "image/" + _ImageMimeType);
         writer.WriteElementString("ImageData", null, _ImageData);

         writer.WriteEndElement(); // EmbeddedImage
      }
   }

   public class DataSet
   {
      private readonly string _Name;
      public bool? AccentSensitivity;
      public bool? CaseSensitivity;
      public string Collation; // Optional
      public List<Field> Fields = new List<Field>(); // Optional
      public bool? KanatypeSensitivity;
      public RDLQuery Query;
      public bool? WidthSensitivity;

      public DataSet(string name, RDLQuery query)
      {
         _Name = name;
         Query = query;
      }

      public void Write(XmlTextWriter writer)
      {
         writer.WriteStartElement("DataSet");
         writer.WriteAttributeString("Name", null, _Name);

         Query.Write(writer);

         if (Fields.Count != 0)
         {
            writer.WriteStartElement("Fields");

            foreach (var field in Fields)
            {
               field.Write(writer);
            }

            writer.WriteEndElement(); // Fields
         }

         writer.WriteEndElement(); // DataSet         
      }
   }

   public class Field
   {
      private readonly string _Name;
      public string DataField; // Optional
      public string Value; // Optional

      public Field(string name)
      {
         _Name = name;
      }

      public void Write(XmlTextWriter writer)
      {
         writer.WriteStartElement("Field");

         writer.WriteAttributeString("Name", null, _Name);
         if(DataField != null)
            writer.WriteElementString("DataField", null, DataField);

         writer.WriteEndElement(); // Field
      }
   }

   public enum CommandTypeT
   {
      Text,
      StoredProcedure,
      TableDirect
   };

   public class RDLQuery
   {
      public string CommandText;
      public CommandTypeT? CommandType;
      public string DataSourceName;
      //note: QueryParameters element not implemented yet
      public uint? Timeout;

      public RDLQuery(string commandText, string dataSourceName)
      {
         CommandText = commandText;
         DataSourceName = dataSourceName;
      }

      public void Write(XmlTextWriter writer)
      {
         writer.WriteStartElement("Query");

         writer.WriteElementString("DataSourceName", DataSourceName);
         writer.WriteElementString("CommandText", CommandText);

         writer.WriteEndElement(); // Query  
      }
   }

   public class DataSource
   {
      private readonly string _Name;
      public RDLConnectionProperties ConnectionProperties; // Optional
      public string DataSourceReference; // Optional
      public bool? Transaction;

      public DataSource(string name)
      {
         _Name = name;
      }

      public void Write(XmlTextWriter writer)
      {
         writer.WriteStartElement("DataSource");
         writer.WriteAttributeString("Name", null, _Name);
         if(ConnectionProperties != null)
            ConnectionProperties.Write(writer);
         writer.WriteEndElement(); // DataSource
      }
   }

   public class RDLConnectionProperties
   {
      public string ConnectString;
      public string DataProvider;
      public bool? IntegratedSecurity;
      public string Prompt; // Optional

      public void Write(XmlTextWriter writer)
      {
         writer.WriteStartElement("ConnectionProperties");

         writer.WriteElementString("DataProvider", DataProvider);
         writer.WriteElementString("ConnectString", ConnectString);

         writer.WriteEndElement(); // ConnectionProperties         
      }

      public RDLConnectionProperties(string connectString, string dataProvider)
      {
         ConnectString = connectString;
         DataProvider = dataProvider;
      }
   } 
}
